using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using FPLibrary;
using FP = GameFrame.Deterministic.FP;
using FPVector2 = GameFrame.Deterministic.FPVector2;
using FPVector3 = GameFrame.Deterministic.FPVector3;

namespace GameFrame
{
    public class FramePrinter
    {
        private int _scope;
        private string _scopeString;
        private FrameBase _frame;
        private readonly StringBuilder _sb;
        private readonly MethodInfo _primitivePrinter;
        private readonly Dictionary<Type, FramePrinter.ReflectObjectType> _reflectObjectLookup;
        private readonly Dictionary<Type, FramePrinter.ReflectStructType> _reflectStructLookup;
        private readonly HashSet<string> _prettyPrintAlwaysTypes = new HashSet<string>((IEnumerable<string>) new string[4]
        {
            "EntityRef",
            "PlayerRef",
            "Button",
            "RNGSession"
        });
        private readonly HashSet<string> _prettyPrintTypes = new HashSet<string>((IEnumerable<string>) new string[14]
        {
            "FP",
            "FPVector2",
            "FPVector3",
            "FPQuaternion",
            "FPBounds2",
            "NullableFP",
            "NullableNonNegativeFP",
            "NullableFPVector2",
            "NullableFPVector3",
            "EntityRef",
            "PlayerRef",
            "Button",
            "RNGSession",
            "QBoolean"
        });

        public static unsafe void EnsurePrimitiveNotStripped<T>() where T : unmanaged => FramePrinter.PrimitivePrinter<T>((string) null, (void*) null, (FramePrinter) null);

        public static void EnsureNotStripped()
        {
            FramePrinter.EnsurePrimitiveNotStripped<bool>();
            FramePrinter.EnsurePrimitiveNotStripped<QBoolean>();
            FramePrinter.EnsurePrimitiveNotStripped<byte>();
            FramePrinter.EnsurePrimitiveNotStripped<sbyte>();
            FramePrinter.EnsurePrimitiveNotStripped<short>();
            FramePrinter.EnsurePrimitiveNotStripped<ushort>();
            FramePrinter.EnsurePrimitiveNotStripped<int>();
            FramePrinter.EnsurePrimitiveNotStripped<uint>();
            FramePrinter.EnsurePrimitiveNotStripped<long>();
            FramePrinter.EnsurePrimitiveNotStripped<ulong>();
            FramePrinter.EnsurePrimitiveNotStripped<IntPtr>();
            FramePrinter.EnsurePrimitiveNotStripped<UIntPtr>();
            FramePrinter.EnsurePrimitiveNotStripped<char>();
            FramePrinter.EnsurePrimitiveNotStripped<double>();
            FramePrinter.EnsurePrimitiveNotStripped<float>();
            FramePrinter.EnsurePrimitiveNotStripped<FP>();
            FramePrinter.EnsurePrimitiveNotStripped<FPVector2>();
            FramePrinter.EnsurePrimitiveNotStripped<FPVector3>();
            FramePrinter.EnsurePrimitiveNotStripped<FPQuaternion>();
            FramePrinter.EnsurePrimitiveNotStripped<FPBounds2>();
            FramePrinter.EnsurePrimitiveNotStripped<NullableFP>();
            FramePrinter.EnsurePrimitiveNotStripped<NullableNonNegativeFP>();
            FramePrinter.EnsurePrimitiveNotStripped<NullableFPVector2>();
            FramePrinter.EnsurePrimitiveNotStripped<NullableFPVector3>();
            FramePrinter.EnsurePrimitiveNotStripped<EntityRef>();
            FramePrinter.EnsurePrimitiveNotStripped<PlayerRef>();
            FramePrinter.EnsurePrimitiveNotStripped<Button>();
            FramePrinter.EnsurePrimitiveNotStripped<RNGSession>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRef>();
            FramePrinter.EnsurePrimitiveNotStripped<Shape2DType>();
            FramePrinter.EnsurePrimitiveNotStripped<Shape3DType>();
            FramePrinter.EnsurePrimitiveNotStripped<JointType>();
            FramePrinter.EnsurePrimitiveNotStripped<JointType3D>();
            FramePrinter.EnsurePrimitiveNotStripped<EntityFlags>();
            FramePrinter.EnsurePrimitiveNotStripped<PhysicsCommon.PhysicsBodyStatus>();
            FramePrinter.EnsurePrimitiveNotStripped<QHashCollection.EntryState>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefCharacterController2DConfig>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefCharacterController3DConfig>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefEntityPrototype>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefEntityView>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefMap>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefNavMesh>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefNavMeshAgentConfig>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefPhysicsMaterial>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefPolygonCollider>();
            FramePrinter.EnsurePrimitiveNotStripped<AssetRefTerrainCollider>();
        }

        public FrameBase Frame => this._frame;

        public bool IsRawPrintEnabled { get; set; }

        public FramePrinter()
        {
            this._sb = new StringBuilder(10485760);
            this._reflectObjectLookup = new Dictionary<Type, FramePrinter.ReflectObjectType>();
            this._reflectStructLookup = new Dictionary<Type, FramePrinter.ReflectStructType>();
            this._primitivePrinter = this.GetType().GetMethod("PrimitivePrinter", BindingFlags.Static | BindingFlags.Public);
            Assert.Always(this._primitivePrinter != (MethodInfo) null);
        }

        public void Reset(FrameBase frame)
        {
            this._frame = frame;
            this._sb.Clear();
            this._scope = 0;
            this._scopeString = (string) null;
        }

        public void LineBegin() => this._sb.Append(this._scopeString);

        public void LineBegin(string text) => this._sb.Append(this._scopeString + text);

        public void LineWrite(string text) => this._sb.Append(text ?? "NULL");

        public unsafe void LineWrite(char* c, int count) => this._sb.Append(c, count);

        public void LineWrite(object text) => this.LineWrite(text == null ? "NULL" : text.ToString());

        public void LineEnd() => this._sb.AppendLine();

        public void LineEnd(string text) => this._sb.AppendLine(text);

        public void AddLine() => this._sb.AppendLine();

        public void AddLine(string line) => this._sb.AppendLine(this._scopeString + (line ?? "NULL"));

        public unsafe void AddLine(char* c, int count)
        {
            this._sb.Append(this._scopeString);
            this._sb.Append(c, count);
            this._sb.AppendLine();
        }

        public void AddLine(object value) => this.AddLine(value == null ? "NULL" : value.ToString());

        public void AddLine(string line, params object[] args) => this.AddLine(string.Format(line, args));

        public unsafe void AddPointer<T>(string name, T* ptr) where T : unmanaged => this.AddPointer(name, (void*) ptr, typeof (T));

        public unsafe void AddPointer(string name, void* ptr, Type type)
        {
            this.AddLine(name + ":");
            this.AddPointerInternal((byte*) ptr, type);
        }

        public bool IsComplexPointerType(Type type) => this.GetReflectStruct(type).Fields != null;

        private unsafe void AddPointerInternal(byte* ptr, Type type)
        {
            this.ScopeBegin();
            foreach (FramePrinter.ReflectStructField field in this.GetReflectStruct(type).Fields)
            {
                if (field.ShouldPrint(ptr))
                {
                    if (field.Collection == null)
                    {
                        FramePrinter.ReflectStructType reflectStruct = this.GetReflectStruct(field.Type);
                        if (reflectStruct.Printer == null)
                        {
                            this.AddLine(field.Name + ": ");
                            this.AddPointerInternal(ptr + field.Offset, field.Type);
                        }
                        else
                            reflectStruct.Printer(field.Name, (void*) (ptr + field.Offset), this);
                    }
                    else
                        this.AddStructCollection(ptr, field);
                }
            }
            this.ScopeEnd();
        }

        private unsafe Ptr ResolvePtrField(byte* ptr, FramePrinter.ReflectStructField rf) => *(Ptr*) (ptr + rf.Offset);

        private static unsafe int ReadPrimitiveFieldValue(byte* ptr, FramePrinter.ReflectStructField rf)
        {
            Type type = rf.Type.IsEnum ? rf.Type.GetEnumUnderlyingType() : rf.Type;
            if (type == typeof (byte))
                return (int) ptr[rf.Offset];
            if (type == typeof (sbyte))
                return (int) (sbyte) ptr[rf.Offset];
            if (type == typeof (short))
                return (int) *(short*) (ptr + rf.Offset);
            if (type == typeof (ushort))
                return (int) *(ushort*) (ptr + rf.Offset);
            if (type == typeof (int))
                return *(int*) (ptr + rf.Offset);
            if (type == typeof (uint))
                return (int) *(uint*) (ptr + rf.Offset);
            throw new InvalidOperationException();
        }

        private unsafe void AddStructCollection(byte* p, FramePrinter.ReflectStructField rf)
        {
            if (rf.Collection is FramePrinter.ReflectStructBuffer collection1)
            {
                Ptr p1 = this.ResolvePtrField(p, rf);
                int num1 = 0;
                for (FramePrinter.ReflectStructBuffer reflectStructBuffer = collection1; reflectStructBuffer != null; reflectStructBuffer = reflectStructBuffer.Next)
                {
                    if (p1 == Ptr.Null)
                    {
                        this.AddLine((reflectStructBuffer.Label ?? rf.Name) + ": NULL");
                    }
                    else
                    {
                        this.AddLine((reflectStructBuffer.Label ?? rf.Name) + ": ");
                        byte* ptr = (byte*) ((IntPtr) this.Frame.Heap->Void(p1) + num1);
                        if (this.IsRawPrintEnabled)
                            this.AddLine(string.Format("  [Ptr]: {0}", (object) p1.Offset));
                        int num2 = reflectStructBuffer.Count.HasValue ? reflectStructBuffer.Count.Value : FramePrinter.ReadPrimitiveFieldValue(p, reflectStructBuffer.CountField);
                        int num3 = MemoryLayoutVerifier.Platform.SizeOf(reflectStructBuffer.Type);
                        FramePrinter.ReflectStructType reflectStruct = this.GetReflectStruct(reflectStructBuffer.Type);
                        if (reflectStruct.Printer == null)
                        {
                            if (reflectStructBuffer.IsSingle)
                            {
                                this.AddPointerInternal(ptr, reflectStructBuffer.Type);
                            }
                            else
                            {
                                this.ScopeBegin();
                                for (int index = 0; index < num2; ++index)
                                    this.AddPointer(string.Format("[{0}]", (object) index), (void*) (ptr + num3 * index), reflectStructBuffer.Type);
                                this.ScopeEnd();
                            }
                        }
                        else if (reflectStructBuffer.IsSingle)
                        {
                            reflectStruct.Printer("", (void*) ptr, this);
                        }
                        else
                        {
                            this.ScopeBegin();
                            for (int index = 0; index < num2; ++index)
                                reflectStruct.Printer(string.Format("[{0}]", (object) index), (void*) (ptr + num3 * index), this);
                            this.ScopeEnd();
                        }
                        if (reflectStructBuffer.Next != null)
                        {
                            int num4 = reflectStructBuffer.Capacity ?? (reflectStructBuffer.CapacityField != null ? FramePrinter.ReadPrimitiveFieldValue(p, reflectStructBuffer.CapacityField) : num2);
                            num1 += num4 * num3;
                        }
                    }
                }
            }
            if (rf.Collection is FramePrinter.ReflectStructList collection2)
            {
                Ptr ptr = this.ResolvePtrField(p, rf);
                if (ptr == Ptr.Null)
                {
                    this.AddLine(rf.Name + ": NULL");
                }
                else
                {
                    this.AddLine(rf.Name + ": ");
                    QListInternal.Print(ptr, this, collection2.Type);
                }
            }
            if (rf.Collection is FramePrinter.ReflectStructHashSet collection3)
            {
                Ptr ptr = this.ResolvePtrField(p, rf);
                if (ptr == Ptr.Null)
                {
                    this.AddLine(rf.Name + ": NULL");
                }
                else
                {
                    this.AddLine(rf.Name + ": ");
                    QHashCollection.Print(ptr, this, collection3.Type);
                }
            }
            if (rf.Collection is FramePrinter.ReflectStructDictionary collection4)
            {
                Ptr ptr = this.ResolvePtrField(p, rf);
                if (ptr == Ptr.Null)
                {
                    this.AddLine(rf.Name + ": NULL");
                }
                else
                {
                    this.AddLine(rf.Name + ": ");
                    QDictionaryInternal.Print(ptr, this, collection4.KeyType, collection4.ValueType);
                }
            }
            if (rf.Collection is FramePrinter.ReflectFixedArray collection5)
            {
                this.AddLine(rf.Name + ": ");
                this.ScopeBegin();
                byte* numPtr = p + rf.Offset;
                int num = MemoryLayoutVerifier.Platform.SizeOf(collection5.Type);
                FramePrinter.ReflectStructType reflectStruct = this.GetReflectStruct(collection5.Type);
                if (reflectStruct.Printer == null)
                {
                    for (int index = 0; index < collection5.Length; ++index)
                        this.AddPointer(string.Format("[{0}]", (object) index), (void*) (numPtr + index * num), collection5.Type);
                }
                else
                {
                    for (int index = 0; index < collection5.Length; ++index)
                        reflectStruct.Printer(string.Format("[{0}]", (object) index), (void*) (numPtr + index * num), this);
                }
                this.ScopeEnd();
            }
            if (!(rf.Collection is FramePrinter.ReflectFixedBuffer collection6))
                return;
            this.AddLine(rf.Name + ": ");
            this.ScopeBegin();
            byte* numPtr1 = p + rf.Offset;
            int num5 = MemoryLayoutVerifier.Platform.SizeOf(collection6.Type);
            FramePrinter.ReflectStructType reflectStruct1 = this.GetReflectStruct(collection6.Type);
            if (reflectStruct1.Printer == null)
            {
                for (int index = 0; index < collection6.Length; ++index)
                    this.AddPointer(string.Format("[{0}]", (object) index), (void*) (numPtr1 + index * num5), collection6.Type);
            }
            else
            {
                for (int index = 0; index < collection6.Length; ++index)
                    reflectStruct1.Printer(string.Format("[{0}]", (object) index), (void*) (numPtr1 + index * num5), this);
            }
            this.ScopeEnd();
        }

        public void AddObject(string name, object value)
        {
            FramePrinter.ReflectObjectType reflectObject = this.GetReflectObject(value.GetType());
            if (reflectObject == null)
            {
                this.AddLine(string.Format("{0}: {1}", (object) name, value));
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(name))
                    this.AddLine(name + ": ");
                this.ScopeBegin();
                foreach (FieldInfo field in reflectObject.Fields)
                {
                    try
                    {
                        this.AddObject(field.Name, field.GetValue(value));
                    }
                    catch (Exception ex)
                    {
                        this.AddLine(field.Name + ": THREW EXCEPTION");
                    }
                }
                this.ScopeEnd();
            }
        }

        public void ScopeBegin()
        {
            ++this._scope;
            this.UpdateScopeString();
        }

        public void ScopeEnd()
        {
            --this._scope;
            this.UpdateScopeString();
        }

        public override string ToString() => this._sb.ToString();

        private void UpdateScopeString()
        {
            if (this._scope > 0)
                this._scopeString = new string(' ', this._scope * 2);
            else
                this._scopeString = "";
        }

        public static unsafe void PrimitivePrinter<T>(string name, void* ptr, FramePrinter printer) where T : unmanaged
        {
            T obj = *(T*) ptr;
            printer.AddLine(name + ": " + obj.ToString());
        }

        private bool IsPrimitive(Type t)
        {
            if (t.IsPrimitive || t.IsEnum)
                return true;
            if (this.IsRawPrintEnabled)
            {
                if (this._prettyPrintAlwaysTypes.Contains(t.Name))
                    return true;
            }
            else if (this._prettyPrintTypes.Contains(t.Name) || t.Name.StartsWith("QString") && t.FindInterfaces((TypeFilter) ((type, _) => type == typeof (IQString) || type == typeof (IQStringUtf8)), (object) null).Length == 1)
                return true;
            return t.Name.StartsWith("AssetRef");
        }

        private FramePrinter.ReflectObjectType GetReflectObject(Type t)
        {
            if (this.IsPrimitive(t))
                return (FramePrinter.ReflectObjectType) null;
            FramePrinter.ReflectObjectType reflectObject;
            if (!this._reflectObjectLookup.TryGetValue(t, out reflectObject))
            {
                reflectObject = new FramePrinter.ReflectObjectType();
                reflectObject.Fields = ((IEnumerable<FieldInfo>) t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).Where<FieldInfo>((Func<FieldInfo, bool>) (x => !x.FieldType.IsPointer)).ToArray<FieldInfo>();
                this._reflectObjectLookup.Add(t, reflectObject);
            }
            return reflectObject;
        }

        public FramePrinter.ReflectStructType GetReflectStruct(Type t)
        {
            FramePrinter.ReflectStructType reflectStruct;
            if (!this._reflectStructLookup.TryGetValue(t, out reflectStruct))
            {
                reflectStruct = new FramePrinter.ReflectStructType();
                if (this.IsPrimitive(t))
                {
                    try
                    {
                        reflectStruct.Printer = (FramePrinter.ReflectStructPrinter) System.Delegate.CreateDelegate(typeof (FramePrinter.ReflectStructPrinter), this._primitivePrinter.MakeGenericMethod(t));
                    }
                    catch (Exception ex)
                    {
                        Log.Exception(ex);
                        Log.Error((object) t);
                    }
                }
                else
                {
                    FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    reflectStruct.Fields = new FramePrinter.ReflectStructField[fields.Length];
                    for (int index = 0; index < fields.Length; ++index)
                        reflectStruct.Fields[index] = this.GetReflectStructField(t, fields[index]);
                }
                this._reflectStructLookup.Add(t, reflectStruct);
            }
            return reflectStruct;
        }

        private FramePrinter.ReflectStructField GetReflectStructField(Type type, FieldInfo field)
        {
            FramePrinter.ReflectStructField reflectStructField = new FramePrinter.ReflectStructField();
            reflectStructField.Type = field.FieldType;
            reflectStructField.Name = field.Name;
            reflectStructField.Offset = MemoryLayoutVerifier.Platform.FieldOffset(field);
            if (reflectStructField.Type == typeof (Ptr))
                reflectStructField.Collection = this.GetReflectStructCollection(type, field);
            if (field.GetCustomAttributes(typeof (FixedBufferAttribute), false).Length != 0)
                reflectStructField.Collection = this.GetReflectStructCollection(type, field);
            object[] customAttributes = field.GetCustomAttributes(typeof (FramePrinter.PrintIfAttribute), false);
            if (customAttributes.Length != 0)
            {
                reflectStructField.PrintConditionals = new FramePrinter.PrintConditional[customAttributes.Length];
                for (int index = 0; index < customAttributes.Length; ++index)
                {
                    FramePrinter.PrintIfAttribute printIfAttribute = customAttributes[index] as FramePrinter.PrintIfAttribute;
                    reflectStructField.PrintConditionals[index] = new FramePrinter.PrintConditional()
                    {
                        Field = this.GetReflectStructField(type, type.GetField(printIfAttribute.Field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)),
                        Value = printIfAttribute.Value,
                        Compare = printIfAttribute.Compare
                    };
                }
            }
            return reflectStructField;
        }

        private FramePrinter.ReflectStructCollection GetReflectStructCollection(
            Type type,
            FieldInfo field)
        {
            object[] customAttributes1 = field.GetCustomAttributes(typeof (FramePrinter.PtrBufferAttribute), false);
            if (customAttributes1.Length >= 1)
            {
                if (customAttributes1.Length > 1)
                    Array.Sort<object>(customAttributes1, (Comparison<object>) ((a, b) => ((FramePrinter.PtrBufferAttribute) b).Order - ((FramePrinter.PtrBufferAttribute) a).Order));
                FramePrinter.ReflectStructBuffer structCollection = (FramePrinter.ReflectStructBuffer) null;
                foreach (FramePrinter.PtrBufferAttribute ptrBufferAttribute in customAttributes1)
                {
                    structCollection = new FramePrinter.ReflectStructBuffer()
                    {
                        Next = structCollection,
                        Type = ptrBufferAttribute.Type,
                        Label = ptrBufferAttribute.Label
                    };
                    if (ptrBufferAttribute.CountFieldName != null)
                        structCollection.CountField = this.GetReflectStructField(type, type.GetField(ptrBufferAttribute.CountFieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
                    if (ptrBufferAttribute.Count > 0)
                        structCollection.Count = new int?(ptrBufferAttribute.Count);
                    if (ptrBufferAttribute.CapacityFieldName != null)
                        structCollection.CapacityField = this.GetReflectStructField(type, type.GetField(ptrBufferAttribute.CapacityFieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
                    if (ptrBufferAttribute.Capacity > 0)
                        structCollection.Capacity = new int?(ptrBufferAttribute.Capacity);
                }
                return (FramePrinter.ReflectStructCollection) structCollection;
            }
            object[] customAttributes2 = field.GetCustomAttributes(typeof (FramePrinter.PtrAttribute), false);
            if (customAttributes2.Length == 1)
            {
                FramePrinter.PtrAttribute ptrAttribute = customAttributes2[0] as FramePrinter.PtrAttribute;
                return (FramePrinter.ReflectStructCollection) new FramePrinter.ReflectStructBuffer()
                {
                    Type = ptrAttribute.Type,
                    IsSingle = true,
                    Count = new int?(1)
                };
            }
            object[] customAttributes3 = field.GetCustomAttributes(typeof (FramePrinter.PtrQListAttribute), false);
            if (customAttributes3.Length == 1)
            {
                FramePrinter.PtrQListAttribute ptrQlistAttribute = customAttributes3[0] as FramePrinter.PtrQListAttribute;
                return (FramePrinter.ReflectStructCollection) new FramePrinter.ReflectStructList()
                {
                    Type = ptrQlistAttribute.Type
                };
            }
            object[] customAttributes4 = field.GetCustomAttributes(typeof (FramePrinter.PtrQHashSetAttribute), false);
            if (customAttributes4.Length == 1)
            {
                FramePrinter.PtrQHashSetAttribute qhashSetAttribute = customAttributes4[0] as FramePrinter.PtrQHashSetAttribute;
                return (FramePrinter.ReflectStructCollection) new FramePrinter.ReflectStructHashSet()
                {
                    Type = qhashSetAttribute.Type
                };
            }
            object[] customAttributes5 = field.GetCustomAttributes(typeof (FramePrinter.PtrQDictionaryAttribute), false);
            if (customAttributes5.Length == 1)
            {
                FramePrinter.PtrQDictionaryAttribute qdictionaryAttribute = customAttributes5[0] as FramePrinter.PtrQDictionaryAttribute;
                return (FramePrinter.ReflectStructCollection) new FramePrinter.ReflectStructDictionary()
                {
                    KeyType = qdictionaryAttribute.KeyType,
                    ValueType = qdictionaryAttribute.ValueType
                };
            }
            object[] customAttributes6 = field.GetCustomAttributes(typeof (FramePrinter.FixedArrayAttribute), false);
            if (customAttributes6.Length == 1)
            {
                FramePrinter.FixedArrayAttribute fixedArrayAttribute = customAttributes6[0] as FramePrinter.FixedArrayAttribute;
                return (FramePrinter.ReflectStructCollection) new FramePrinter.ReflectFixedArray()
                {
                    Type = fixedArrayAttribute.Type,
                    Length = fixedArrayAttribute.Length
                };
            }
            object[] customAttributes7 = field.GetCustomAttributes(typeof (FixedBufferAttribute), false);
            if (customAttributes7.Length != 1)
                return (FramePrinter.ReflectStructCollection) null;
            FixedBufferAttribute fixedBufferAttribute = customAttributes7[0] as FixedBufferAttribute;
            return (FramePrinter.ReflectStructCollection) new FramePrinter.ReflectFixedBuffer()
            {
                Type = fixedBufferAttribute.ElementType,
                Length = fixedBufferAttribute.Length
            };
        }

        public unsafe delegate void ReflectStructPrinter(string name, void* ptr, FramePrinter printer);

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
        public class PtrQListAttribute : Attribute
        {
            public Type Type { get; }

            public PtrQListAttribute(Type type) => this.Type = type;
        }

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
        public class PtrQHashSetAttribute : Attribute
        {
            public Type Type { get; }

            public PtrQHashSetAttribute(Type type) => this.Type = type;
        }

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
        public class PtrQDictionaryAttribute : Attribute
        {
            public Type KeyType { get; }

            public Type ValueType { get; }

            public PtrQDictionaryAttribute(Type keyType, Type valueType)
            {
                this.KeyType = keyType;
                this.ValueType = valueType;
            }
        }

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
        public class PtrBufferAttribute : Attribute
        {
            public Type Type { get; }

            public string CountFieldName { get; }

            public int Count { get; }

            public string CapacityFieldName { get; set; }

            public int Capacity { get; set; }

            public string Label { get; set; }

            public int Order { get; set; }

            public PtrBufferAttribute(Type type, string countFieldName)
            {
                this.Type = type;
                this.CountFieldName = countFieldName;
                this.Count = -1;
            }

            public PtrBufferAttribute(Type type, int fixedCount)
            {
                this.Type = type;
                this.CountFieldName = (string) null;
                this.Count = fixedCount;
            }
        }

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
        public class PtrAttribute : Attribute
        {
            public Type Type { get; }

            public PtrAttribute(Type type) => this.Type = type;
        }

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
        public class FixedArrayAttribute : Attribute
        {
            public Type Type { get; }

            public int Length { get; }

            public FixedArrayAttribute(Type type, int length)
            {
                this.Type = type;
                this.Length = length;
            }
        }

        [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
        public class PrintIfAttribute : Attribute
        {
            public string Field { get; }

            public int Value { get; }

            public FramePrinter.PrintIfAttribute.CompareOperator Compare { get; }

            public PrintIfAttribute(
                string fieldName,
                int value,
                FramePrinter.PrintIfAttribute.CompareOperator compare = FramePrinter.PrintIfAttribute.CompareOperator.Equal)
            {
                this.Field = fieldName;
                this.Value = value;
                this.Compare = compare;
            }

            public enum CompareOperator
            {
                Equal,
                NotEqual,
                Less,
                LessOrEqual,
                GreaterOrEqual,
                Greater,
            }
        }

        public abstract class ReflectStructCollection
        {
        }

        public class ReflectStructBuffer : FramePrinter.ReflectStructCollection
        {
            public Type Type;
            public FramePrinter.ReflectStructField CountField;
            public int? Count;
            public bool IsSingle;
            public FramePrinter.ReflectStructField CapacityField;
            public int? Capacity;
            public string Label;
            public FramePrinter.ReflectStructBuffer Next;
        }

        public class ReflectStructList : FramePrinter.ReflectStructCollection
        {
            public Type Type;
        }

        public class ReflectStructHashSet : FramePrinter.ReflectStructCollection
        {
            public Type Type;
        }

        public class ReflectStructDictionary : FramePrinter.ReflectStructCollection
        {
            public Type KeyType;
            public Type ValueType;
        }

        public class ReflectFixedArray : FramePrinter.ReflectStructCollection
        {
            public Type Type;
            public int Length;
        }

        public class ReflectFixedBuffer : FramePrinter.ReflectStructCollection
        {
            public Type Type;
            public int Length;
        }

        public class PrintConditional
        {
            public FramePrinter.ReflectStructField Field;
            public int Value;
            public FramePrinter.PrintIfAttribute.CompareOperator Compare;

            public unsafe bool ShouldPrint(byte* ptr)
            {
                int num;
                try
                {
                    num = FramePrinter.ReadPrimitiveFieldValue(ptr, this.Field);
                }
                catch
                {
                    return true;
                }
                switch (this.Compare)
                {
                    case FramePrinter.PrintIfAttribute.CompareOperator.Equal:
                        return num.Equals(this.Value);
                    case FramePrinter.PrintIfAttribute.CompareOperator.NotEqual:
                        return !num.Equals(this.Value);
                    case FramePrinter.PrintIfAttribute.CompareOperator.Less:
                        return num < this.Value;
                    case FramePrinter.PrintIfAttribute.CompareOperator.LessOrEqual:
                        return num <= this.Value;
                    case FramePrinter.PrintIfAttribute.CompareOperator.GreaterOrEqual:
                        return num >= this.Value;
                    case FramePrinter.PrintIfAttribute.CompareOperator.Greater:
                        return num > this.Value;
                    default:
                        return true;
                }
            }
        }

        public class ReflectStructField
        {
            public int Offset;
            public Type Type;
            public string Name;
            public FramePrinter.PrintConditional[] PrintConditionals;
            public FramePrinter.ReflectStructCollection Collection;

            public unsafe bool ShouldPrint(byte* ptr)
            {
                if (this.PrintConditionals == null)
                    return true;
                foreach (FramePrinter.PrintConditional printConditional in this.PrintConditionals)
                {
                    if (!printConditional.ShouldPrint(ptr))
                        return false;
                }
                return true;
            }
        }

        public class ReflectStructType
        {
            public FramePrinter.ReflectStructField[] Fields;
            public FramePrinter.ReflectStructPrinter Printer;
        }

        private class ReflectObjectType
        {
            public FieldInfo[] Fields;
        }
    }
}